﻿
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace DynamicCode.Compilation
{
    public abstract class BaseDynamicCodeBuilder : IDynamicCodeBuilder
    {
        public BaseDynamicCodeBuilder(string codetemplate)
        {
            if (string.IsNullOrWhiteSpace(codetemplate))
            {
                throw new ArgumentException($"“{nameof(codetemplate)}”不能为 null 或空白。", nameof(codetemplate));
            }
            if (string.IsNullOrWhiteSpace(codetemplate))
            {
                throw new ArgumentException($"“{nameof(codetemplate)}”不能为 null 或空白。", nameof(codetemplate));
            }
            if (!codetemplate.Contains("@code@")) throw new DynamicCodeException($"“{nameof(codetemplate)}”不存在 @code@");
            if (!codetemplate.Contains("@content@")) throw new DynamicCodeException($"“{nameof(codetemplate)}”不存在 @content@");
            CodeTemplate = codetemplate;
        }
        public string CodeTemplate { get; private set; }
        protected virtual void Verification(IDictionary<string, string> variable)
        {

        }
        protected string Replace(string str, IDictionary<string, string> pairs, bool lineAppendSemicolon = false)
        {
            string tmp = str;
            foreach (var item in pairs)
            {
                tmp = tmp.Replace(item.Key, item.Value);
            }
            if (lineAppendSemicolon)
            {
                StringBuilder stringBuilder = new StringBuilder();
                foreach (var item in tmp.Split(Environment.NewLine))
                {
                    if (item.TrimEnd().EndsWith(";"))
                    {
                        stringBuilder.AppendLine(item);
                        continue;
                    }
                    stringBuilder.Append(item);
                    stringBuilder.Append(";\r\n");

                }

                return stringBuilder.ToString();

            }
            return tmp;
        }
        public virtual Type Grenerate(DynamicCodeItem item)
        {

            var type = Grenerate(new DynamicCodeItem[] { item })?.FirstOrDefault();
            return type;
        }

        public abstract Type[] Grenerate(DynamicCodeItem[] items);
        protected abstract IDictionary<string, string> FindVariable(string str);

        protected virtual Type[] GenerateCode(params string[] codes)
        {
            if (codes == null) return new Type[0];
            var trees = codes.Select(code => CSharpSyntaxTree.ParseText(code));
            var dd = typeof(Enumerable).GetTypeInfo().Assembly.Location;
            var coreDir = Directory.GetParent(dd);
            var references = AppDomain.CurrentDomain.GetAssemblies().Where(n => !string.IsNullOrEmpty(n.Location)).Select(n => MetadataReference.CreateFromFile(n.Location)).ToList();

            var compilation = CSharpCompilation.Create(Path.GetRandomFileName(),
                syntaxTrees: trees,
                references: references,
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                );

            using (var ms = new MemoryStream())
            {
                var result = compilation.Emit(ms);
                if (result.Success)
                {
                    ms.Seek(0, System.IO.SeekOrigin.Begin);
                    var ass = Assembly.Load(ms.ToArray());
                    return ass.GetTypes();

                }
                else
                {
                    var arr = result.Diagnostics.Where(n => n.DefaultSeverity == DiagnosticSeverity.Error).Select(n => n.ToString());
                    throw new DynamicCodeException(String.Join(Environment.NewLine, arr));

                }
            }
        }
    }
}
